TOOL.Category = "Conna's Tools"
TOOL.Name = "#Cloaking Tool"
TOOL.Command = nil
TOOL.ConfigName = ""

// Tables

local Cloaking = {}

// ConVars

TOOL.ClientConVar["Key"] = "5"
TOOL.ClientConVar["Flicker"] = "0"
TOOL.ClientConVar["Inversed"] = "0"
TOOL.ClientConVar["Material"] = "sprites/heatwave"

// Client

if (CLIENT) then
	language.Add("Tool_cloaking_name", "Cloaking Tool")
	language.Add("Tool_cloaking_desc", "Allows you to cloak entities by pressing a key")
	language.Add("Tool_cloaking_0", "Left click on an entity to set cloaking for it and connected entities")
	
	language.Add("Undone_cloaking", "Undone Cloaking")
end

// Message

function TOOL:Message(Text)
	if SERVER then
		self:GetOwner():SendLua("GAMEMODE:AddNotify('"..Text.."', NOTIFY_GENERIC, 10)")
		self:GetOwner():SendLua("surface.PlaySound('ambient/water/drip"..math.random(1, 4)..".wav')")
	end
end

// Get entities

local function ExtractEntities(Entity, Entities, Constraints)
	Constraints = Constraints or {}
	
	Entities = Entities or {}
	
	if (!Entity:IsValid()) then return Entities, Constraints end

	local Index = Entity:EntIndex()
	
	Entities[Index] = Entity
	
	if (!constraint.HasConstraints(Entity)) then return Entities, Constraints end
	
	local Cons = constraint.GetTable(Entity)
	
	for K, V in pairs(Cons) do
		local Index = V.Constraint
		
		if (!Constraints[Index]) then
			Constraints[Index] = V.Constraint
			
			for B, J in pairs(V.Entity) do
				local E, C = ExtractEntities(J.Entity, Entities, Constraints)
				
				table.Merge(Entities, E)
				table.Merge(Constraints, C)
			end
		end
	end
	
	return Entities, Constraints
end

// Cloaking

local function SetCloaking(Player, Entity, Data)
	Entity.Cloaking = {}
	
	// Wire
	
	if (Wire_CreateOutputs) then Entity.Outputs = Wire_CreateOutputs(Entity, {"Active"}) end
	
	// Flicker and material
	
	Entity.Cloaking.Cloaked = false
	Entity.Cloaking.Flicker = Data.Flicker
	Entity.Cloaking.Material = Data.Material
	Entity.Cloaking.Inverse = Data.Inverse
	
	// Inverse
	
	if (Data.Inverse) then
		Cloaking.Activate(Entity)
	else
		Cloaking.Deactivate(Entity)
	end
	
	// Wire
	
	local Number = 0
	
	if (Entity.Cloaking.Cloaked) then Number = 1 end
	
	if (Wire_TriggerOutput) then Wire_TriggerOutput(Entity, "Active", Number) end
	
	// Numpad
	
	Entity.Cloaking.ID = numpad.OnUp(Player, Data.Key, "Cloaking.Pressed", Entity, false)
	
	// Store entity modifier
	
	duplicator.StoreEntityModifier(Entity, "Cloaking", Data)

	return true
end

duplicator.RegisterEntityModifier("Cloaking", SetCloaking)

// Left click

function TOOL:LeftClick(Trace)
	if Trace.Entity then
		if !Trace.Entity:IsValid() or Trace.Entity:IsPlayer() or Trace.HitWorld or Trace.Entity:IsNPC() then
			return false
		end
	end
	
	if(CLIENT) then
		return true
	end

	if(!SERVER) then return false end

	local Key = self:GetClientNumber("Key")
	local Flicker = self:GetClientNumber("Flicker")
	local Inversed = self:GetClientNumber("Inversed")
	local Material = self:GetClientInfo("Material")
	
	local Entities, Constraints = ExtractEntities(Trace.Entity)
	
	// Player
	
	local Player = self:GetOwner()
	
	// Set cloaking
	
	for K, V in pairs(Entities) do
		SetCloaking(Player, V, {Key = Key, Flicker = (Flicker == 1), Inverse = (Inversed == 1), Material = Material})
	end
	
	// Undo
	
	local function Function(Undo, Table)
		for K, V in pairs(Table) do
			local Valid = V:IsValid()
			
			if (Valid) then
				Cloaking.Deactivate(V)
				
				numpad.Remove(V.Cloaking.ID)
				
				V.Cloaking = nil
			end
		end
	end
	
	undo.Create("cloaking")
		undo.SetPlayer(Player)
		undo.AddFunction(Function, Entities)
	undo.Finish()
	
	// Message
	
	self:Message("Cloaking added to entities!")
	
	return true
end

// Activate

function Cloaking.Activate(Entity)
	Entity.Cloaking.Previous = Entity:GetMaterial()
	
	Entity:DrawShadow(false)
	Entity:SetMaterial(Entity.Cloaking.Material)
	
	Entity.Cloaking.Cloaked = true
	
	if (Wire_TriggerOutput) then
		Wire_TriggerOutput(Entity, "Active", 1)
	end
end

// Deactivate

function Cloaking.Deactivate(Entity)
	local Material = Entity.Cloaking.Previous or ""
	
	Entity:DrawShadow(true)
	Entity:SetMaterial(Material)
	
	Entity.Cloaking.Cloaked = false
	
	if (Wire_TriggerOutput) then
		Wire_TriggerOutput(Entity, "Active", 0)
	end
end

// Pressed key

if SERVER then
	function TOOL.Pressed(Player, Entity, Key, IDX)
		if !Entity:IsValid() then return end
		
		if not Key then
			if (Entity.Cloaking.Cloaked) then
				Cloaking.Deactivate(Entity)
			else
				Cloaking.Activate(Entity)
			end
		end
	end
	
	numpad.Register("Cloaking.Pressed", TOOL.Pressed)
	
	// Entity takes damage
	
	function TOOL.TakeDamage(Entity, Attacker, Inflictor, Damage)
		if (Entity.Cloaking and Entity.Cloaking.Cloaked) then
			if (Entity.Cloaking.Flicker) then
				Cloaking.Deactivate(Entity)
				
				timer.Simple(0.05, Cloaking.Activate, Entity)
			end
		end
	end

	hook.Add("EntityTakeDamage", "Cloaking.TakeDamage", TOOL.TakeDamage)
end

// Build CPanel

function TOOL.BuildCPanel(Panel)
	Panel:AddControl("Header", {Text = "#Tool_cloaking_name", Description	= "#Tool_cloaking_desc"})
	
	Panel:AddControl("Numpad", {Label = "Button", ButtonSize = "22", Command = "cloaking_Key"})
	
	local Options = {
	}
	
	local Materials = {
		{"Heatwave", "sprites/heatwave"},
		{"Light", "Models/effects/vol_light001"}
	}
	
	for K, V in pairs(Materials) do
		local ID = V[1]
		
		Options[ID] = {cloaking_Material = V[2]}
	end
	
	Panel:AddControl("ListBox", {Label = "#Choose material", MenuButton = 0, Height = 120, Options = Options})
	
	Panel:AddControl("Checkbox", {Label = "#Inversed (Start Activated)", Command = "cloaking_Inversed"})
	
	Panel:AddControl("Checkbox", {Label = "#Flicker when shot", Command = "cloaking_Flicker"})
end